package com.lobstar.xrpc.common;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;

import com.lobstar.xrpc.common.ClientCallable.ResponseHolder;
import com.lobstar.xrpc.common.exception.InvokeException;
import com.lobstar.xrpc.common.po.SMessage;
import com.lobstar.xrpc.server.handler.task.ReturnHandler;
import com.lobstar.xrpc.server.handler.task.TaskCallback;
import com.lobstar.xrpc.service.utils.JsonUtils;
import com.lobstar.xrpc.tools.Fields;

public interface ServerCallable {
	public int responseWaitTimeout = 10;
	public default <T> T asyncCall(String targetClient,String method,Fields field,Type clz) throws Exception{
		ReentrantLock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		ResponseHolder<T> ret = new ResponseHolder<T>();
		StringBuilder _ex = new StringBuilder();
		if(online(targetClient)) {
    		lock.lock();
			SMessage message = new SMessage().buildRequest().method(method).addAttributes(field.fields());
			TransformObject<T> transformObject = new TransformObject<T>() {
				@Override
				public void handle(ResponseHolder<T> ret, String json) {
					try {
						ret.fill(JsonUtils.toObject(json, clz));
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			};
			call(targetClient,message, new TaskCallback() {
				@Override
				public void callback(String json) {
					lock.lock();
					transformObject.handle(ret, json);
					condition.signal();
					lock.unlock();
				}
				@Override
				public void handleException(String exception) {
					lock.lock();
					_ex.append(exception);
					condition.signal();
					lock.unlock();
				}
			});
			condition.await(responseWaitTimeout,TimeUnit.SECONDS);
			lock.unlock();
			if(!"".equals(_ex.toString())) {
				throw new InvokeException(_ex.toString());
			}
    	}else {
    		throw new RuntimeException("client not online");
    	}
		return ret.get();
	}

	public default void asyncCall(String targetClient,String method,Fields field) throws Exception{
		ReentrantLock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		StringBuilder _ex = new StringBuilder();
		if(online(targetClient)) {
    		lock.lock();
			SMessage message = new SMessage().buildRequest().method(method).addAttributes(field.fields());
			call(targetClient,message, new TaskCallback() {
				@Override
				public void callback(String json) {
					lock.lock();
					condition.signal();
					lock.unlock();
				}
				@Override
				public void handleException(String exception) {
					lock.lock();
					_ex.append(exception);
					condition.signal();
					lock.unlock();
				}
			});
			condition.await(responseWaitTimeout,TimeUnit.SECONDS);
			lock.unlock();
			if(!"".equals(_ex.toString())) {
				throw new InvokeException(_ex.toString());
			}
    	}else {
    		throw new RuntimeException("client not online");
    	}
	}
	public default void syncCall(String targetClient,String method,Fields field) {
		if(online(targetClient)) {
			SMessage message = new SMessage().buildRequest().method(method).addAttributes(field.fields());
			call(targetClient,message,null);
		}else {
			throw new RuntimeException("client not online");
		}
	}
	public default <T> void syncCall(String targetClient,String method,Fields field,Class<T> clz,ReturnHandler<T> callback) {
		if(online(targetClient)) {
			SMessage message = new SMessage().buildRequest().method(method).addAttributes(field.fields());
			call(targetClient,message, new TaskCallback() {
				@Override
				public void callback(String json) {
					try {
						callback.handle(JsonUtils.toObject(json, clz));
					}catch(Exception e) {
						e.printStackTrace();
					}
				}
				@Override
				public void handleException(String exception) {
					callback.exception(exception);
				}
			});
		}else {
			throw new RuntimeException("client not online");
		}
	}

	public void call(String targetClient,SMessage message, TaskCallback callback);
	public boolean online(String targetClient);
	interface TransformObject<T> {
		public void handle(ResponseHolder<T> ret,String json);
	}
	static class ResponseHolder<T> {
		T object;
		void fill(T t) {
			ResponseHolder<T> rh = new ResponseHolder<T>();
			rh.object=t;
		}
		T get() {
			return object;
		}
	}
}
